package com.videri.match.client;

import java.util.Arrays;
import java.util.Collections;

import com.allen_sauer.gwt.dragdrop.client.PickupDragController;
import com.allen_sauer.gwt.dragdrop.client.drop.DropController;
import com.allen_sauer.gwt.dragdrop.client.drop.NoOverlapDropController;
import com.allen_sauer.gwt.dragdrop.client.util.DOMUtil;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Random;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FocusPanel;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.HorizontalSplitPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.HasHorizontalAlignment.HorizontalAlignmentConstant;
import com.google.gwt.user.client.ui.*;
import com.google.gwt.core.client.*;
import com.google.gwt.user.client.rpc.*;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.Timer;

/**
 * This class will allow users to participate in head to head
 * games against other users.  It will then notify the user at
 * the conclusion whether they won the game or not.
 * @author Michael Bauer
 * @author Patrick Eibl
 */
public class Head2Head extends MatchSink {

	private AbsolutePanel myAbsolutePanel = new AbsolutePanel();
	private ImageRegion[] myImageRegions;
	private StringBoxPair[] myStringBoxes;
	private static final String CSS_LABEL_STYLE = "match-string-style";
	private int head2headGameID;
	private int playerID;
	private int waitCount;
	private Timer waitForOtherPlayer;
	
	public static MatchSinkInfo init(){
		return new MatchSinkInfo("Play Head-to-Head") {
			public MatchSink createInstance(){
				return new Head2Head();
			}

			public String getColor(){
				// You can change this to change the color of the tab for this option
				return "#AE2F2D";
			}
		};
	}
	
	public Head2Head(){
		initWidget(myAbsolutePanel);
		head2headGameID = -1;
		playerID = Random.nextInt(1000000);
	}
	
	public void onShow(){
		waitCount = 500;
		Head2HeadServiceAsync head2headServices = (Head2HeadServiceAsync)GWT.create(Head2HeadService.class);
		
		  ServiceDefTarget endpoint = (ServiceDefTarget) head2headServices;
		  String moduleRelativeURL = GWT.getModuleBaseURL()+"head2head";
		  endpoint.setServiceEntryPoint(moduleRelativeURL);
		  
		  AsyncCallback callback = new AsyncCallback() {
			  public void onSuccess(Object result){
				  head2headGameID = ((Integer)result).intValue();
				  waitForOtherPlayer = new Timer() {
					  public void run(){
						  waitForServlet();
					  }
				  };
				  waitForOtherPlayer.scheduleRepeating(1000);
				  /*if(head2headGameID == -1)
					  waitForServlet();
				  else{
					  final int gameNum = head2headGameID;
					  VideriMatchServiceAsync videriServices = (VideriMatchServiceAsync)GWT.create(VideriMatchService.class);
						
					  ServiceDefTarget endpoint = (ServiceDefTarget) videriServices;
					  String moduleRelativeURL = GWT.getModuleBaseURL()+"matching";
					  endpoint.setServiceEntryPoint(moduleRelativeURL);
					  
					  AsyncCallback callback = new AsyncCallback() {
						  public void onSuccess(Object result){
							  Matching m = (Matching)result;
							  initialize2(m);
						  }
						  public void onFailure(Throwable caught){
							  System.err.println("Warning: unable to locate head to head matching!");
						  }
					  };
					  videriServices.getHead2HeadMatching(gameNum, callback);
				  }*/
			  }
			  public void onFailure(Throwable caught){
				  System.err.println("Warning: unable to access the server!");
			  }
		  };		  
		  head2headServices.checkForWaitingPlayer(playerID, callback);
	}
	
	public void onHide(){
		head2headGameID = -1;
		playerID = -1;
		myAbsolutePanel.clear();
	}
	
	private void waitForServlet(){
		VideriMatchServiceAsync videriServices = (VideriMatchServiceAsync)GWT.create(VideriMatchService.class);
		
		  ServiceDefTarget endpoint = (ServiceDefTarget) videriServices;
		  String moduleRelativeURL = GWT.getModuleBaseURL()+"matching";
		  endpoint.setServiceEntryPoint(moduleRelativeURL);
		  
		AsyncCallback callback = new AsyncCallback() {                
			public void onSuccess(Object result) {
				head2headGameID = ((Integer)result).intValue();
				
				if(head2headGameID == -1){
					//waitForServlet();
				}
				else{
					waitForOtherPlayer.cancel();
					final int gameNum = head2headGameID;

					VideriMatchServiceAsync videriServices = (VideriMatchServiceAsync)GWT.create(VideriMatchService.class);

					ServiceDefTarget endpoint = (ServiceDefTarget) videriServices;
					String moduleRelativeURL = GWT.getModuleBaseURL()+"matching";
					endpoint.setServiceEntryPoint(moduleRelativeURL);

					AsyncCallback callback = new AsyncCallback() {
						public void onSuccess(Object result){
							Matching m = (Matching)result;
							initialize2(m);
						}
						public void onFailure(Throwable caught){
							System.err.println("Warning: unable to locate head to head matching!");
						}
					};

					videriServices.getHead2HeadMatching(gameNum, callback);
				}
			}                
			public void onFailure(Throwable caught) {                    
				System.err.println("Warning: unable to start head to head game!");
			}            
		};
		videriServices.startNewHead2HeadGame(playerID, callback);
	}
	
	private void initialize2(final Matching m){
		System.out.println(m.getName());
		myAbsolutePanel.clear();
//		HorizontalSplitPanel hsp = new HorizontalSplitPanel();
		HorizontalPanel hp = new HorizontalPanel();
		final VerticalPanel vp = new VerticalPanel();
		vp.setWidth("20em");
		vp.add(new Button("Check Solution", new ClickListener() {
			public void onClick(Widget sender) {
				boolean winner = true;
				int i=0;
				Widget[] w;
				switch(m.my2Type) {
				case Matching.ImageRegion:
					while(winner && i<myImageRegions.length) {
						w = myImageRegions[i].getDropped();
						if (w.length > 0)
							winner = m.isMatch(((Label)(w[0])).getText(),myImageRegions[i].getRegion());
						else winner = false;
						i++;
					}
					break;
				case Matching.String:
					while(winner && i<m.myStrings2.length) {
						w = myStringBoxes[i].getDropped();
						if (w.length > 0)
							winner = m.isMatch(((Label)(w[0])).getText(),m.myStrings2[i]);
						else winner = false;
						i++;
					}
					break;
				case Matching.Image:
					while(winner && i<m.myImages2.length) {
						w = myStringBoxes[i].getDropped();
						if (w.length > 0)
							winner = m.isMatch(((Label)(w[0])).getText(),new Image(m.myImages2[i]));
						else winner = false;
						i++;
					}
					break;
				}
				if(winner){
					final int gameNum = head2headGameID;
					VideriMatchServiceAsync videriServices = (VideriMatchServiceAsync)GWT.create(VideriMatchService.class);
					
					ServiceDefTarget endpoint = (ServiceDefTarget)videriServices;
					String moduleRelativeURL = GWT.getModuleBaseURL()+"matching";
					endpoint.setServiceEntryPoint(moduleRelativeURL);
					
					AsyncCallback callback = new AsyncCallback() {
						public void onSuccess(Object result){
							boolean won = ((Boolean)result).booleanValue();
							if(!won){
								vp.add(new Label("WINNER!!!"));
							}
							else{
								vp.add(new Label("LOSER!!!"));
							}
						}
						public void onFailure(Throwable caught){
							System.err.println("Warning: Unable to verify outcome with server!");
						}
					};
					videriServices.checkHead2HeadGameWon(gameNum, callback);
				}
				else{
					vp.add(new Label("PUZZLE INCORRECT!  TRY AGAIN!"));
					initialize2(m);
				}
			}
		}));
		
		SourceListDropController sourceList = new SourceListDropController(vp);

		PickupDragController pdc = new PickupDragController(myAbsolutePanel, false);
		pdc.registerDropController(sourceList);

		System.out.println("Panel setup complete");

		//Set up left side
		switch(m.my1Type)
		{
		case Matching.String: //If we're matching Strings to X:
			for(int i = 0; i < m.myStrings1.length; i++)
			{
				Label l = new Label(m.myStrings1[i], false);
				l.addStyleName(CSS_LABEL_STYLE);
				l.setWidth(l.getText().length() + "em");
				l.setHorizontalAlignment(Label.ALIGN_CENTER);
				//vp.add(l);
				sourceList.addWidget(l);
				pdc.makeDraggable(l);
			}
			break;
		case Matching.Image:
			for(int i = 0; i < m.myImages1.length; i++)
			{
				Image ii = new Image(m.myImages1[i]);
				vp.add(ii);
				pdc.makeDraggable(ii);
			}
			break;
		default:
			System.err.println("PlayGame Error: m.my1Type is Invalid ("+m.my1Type+")");
		break;
		}
//		hsp.setLeftWidget(vp);
		hp.add(vp);
		System.out.println("Left Set Up");

		//Set Up Right Side
		AbsolutePanel ap2 = new AbsolutePanel();
		NoOverlapDropController nodc = new NoOverlapDropController(ap2);

		pdc.registerDropController(nodc);

		switch(m.my2Type)
		{
		case Matching.String:
			int[] s = shuffledList(m.myStrings2.length);
			myStringBoxes = new StringBoxPair[m.myStrings2.length];
			for(int i = 0; i < m.myStrings2.length; i++)
			{
				StringBoxPair sbp = new StringBoxPair(m.myStrings2[s[i]],Random.nextInt(255),Random.nextInt(255),Random.nextInt(255));
				ap2.add(sbp);
				pdc.registerDropController(sbp.getDropController());
				myStringBoxes[s[i]] = sbp;
			}
			break;
		case Matching.ImageRegion:
			ap2.add(m.getBackground(),-1,-1);
			myImageRegions = new ImageRegion[m.myImageRegions2.length];
			for(int i = 0; i < m.myImageRegions2.length; i++)
			{
				ImageRegion ir = new ImageRegion(m.myImageRegions2[i], Random.nextInt(255), Random.nextInt(255), Random.nextInt(255));
				pdc.registerDropController(ir.getDropController());
				addImageRegion(ap2, ir);
				myImageRegions[i] = ir;
			}
			break;
		case Matching.Image:
			int[] s2 = shuffledList(m.myImages2.length);
			myStringBoxes = new StringBoxPair[m.myImages2.length];
			for(int i = 0; i < m.myImages2.length; i++)
			{
				StringBoxPair sbp = new StringBoxPair(new Image(m.myImages2[s2[i]]),Random.nextInt(255),Random.nextInt(255),Random.nextInt(255));
				ap2.add(sbp);
				pdc.registerDropController(sbp.getDropController());
				myStringBoxes[s2[i]] = sbp;
			}
			break;
		}
//		hsp.setRightWidget(ap2);
		hp.add(ap2);
		myAbsolutePanel.add(hp,-1,-1);
		System.out.println("Right Set Up");
	}

	public static void addImageRegion(AbsolutePanel sp, ImageRegion ir)
	{
		sp.add(ir, ir.getX(), ir.getY());
	}
	
	/**
	 * creates a shuffled array of ints from 0 to
	 * length-1
	 * @param length
	 * @return
	 */
	public static int[] shuffledList(int length) {
		int[] list = new int[length];
		for(int i=0;i<length;i++) list[i] = i;
		for(int i=0;i<length;i++) swapRandom(list);
		return list;
	}
	
	/**
	 * swaps two random elements of list
	 * @param list
	 */
	public static void swapRandom(int[] list) {
		int i1 = Random.nextInt(list.length);
		int i2 = Random.nextInt(list.length);
		int temp = list[i1];
		list[i1] = list[i2];
		list[i2] = temp;
	}
}
